#include "surfaceglwidget.h"

#include <QMouseEvent>
#include <cmath>

// 顶点着色器
static const char *vertexShaderSource =
    "attribute vec3 position;\n"
    "attribute vec3 color;\n"
    "varying vec3 vColor;\n"
    "uniform mat4 mvpMatrix;\n"
    "void main() {\n"
    "    gl_Position = mvpMatrix * vec4(position, 1.0);\n"
    "    vColor = color;\n"
    "}\n";

// 片段着色器
static const char *fragmentShaderSource =
    "varying vec3 vColor;\n"
    "void main() {\n"
    "    gl_FragColor = vec4(vColor, 1.0);\n"
    "}\n";

SurfaceGLWidget::SurfaceGLWidget(QWidget *parent)
    : QOpenGLWidget(parent),
      m_program(nullptr),
      m_xRot(0),
      m_yRot(0),
      m_zRot(0),
      m_scale(1.0f) {}

SurfaceGLWidget::~SurfaceGLWidget() {
    makeCurrent();
    m_vbo.destroy();
    m_vao.destroy();
    delete m_program;
    doneCurrent();
}

void SurfaceGLWidget::initializeGL() {
    initializeOpenGLFunctions();
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glEnable(GL_DEPTH_TEST);

    initShaders();
    initSurfaceData();

    // 初始化变换矩阵
    m_projection.setToIdentity();
    m_projection.perspective(45.0f, width() / float(height()), 0.1f, 100.0f);

    m_view.setToIdentity();
    m_view.translate(0.0f, 0.0f, -5.0f);

    m_model.setToIdentity();
}

void SurfaceGLWidget::paintGL() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    m_program->bind();
    m_vao.bind();

    // 更新模型矩阵
    m_model.setToIdentity();
    m_model.scale(m_scale);
    m_model.rotate(m_xRot, 1.0f, 0.0f, 0.0f);
    m_model.rotate(m_yRot, 0.0f, 1.0f, 0.0f);
    m_model.rotate(m_zRot, 0.0f, 0.0f, 1.0f);

    // 计算并设置MVP矩阵
    QMatrix4x4 mvp = m_projection * m_view * m_model;
    m_program->setUniformValue("mvpMatrix", mvp);

    // 绘制曲面
    glDrawArrays(GL_TRIANGLE_STRIP, 0, m_vertices.size() / 3);

    m_vao.release();
    m_program->release();
}

void SurfaceGLWidget::resizeGL(int w, int h) {
    m_projection.setToIdentity();
    m_projection.perspective(45.0f, w / float(h), 0.1f, 100.0f);
}

void SurfaceGLWidget::mousePressEvent(QMouseEvent *event) {
    m_lastPos = event->pos();
}

void SurfaceGLWidget::mouseMoveEvent(QMouseEvent *event) {
    int dx = event->pos().x() - m_lastPos.x();
    int dy = event->pos().y() - m_lastPos.y();

    if (event->buttons() & Qt::LeftButton) {
        m_xRot += dy;
        m_yRot += dx;
        update();
    }
    m_lastPos = event->pos();
}

void SurfaceGLWidget::wheelEvent(QWheelEvent *event) {
    QPoint numDegrees = event->angleDelta() / 8;

    if (!numDegrees.isNull()) {
        m_scale += numDegrees.y() / 100.0f;
        if (m_scale < 0.1f) m_scale = 0.1f;
        if (m_scale > 10.0f) m_scale = 10.0f;
        update();
    }

    event->accept();
}

void SurfaceGLWidget::initShaders() {
    m_program = new QOpenGLShaderProgram;

    // 编译并链接着色器程序
    if (!m_program->addShaderFromSourceCode(QOpenGLShader::Vertex,
                                            vertexShaderSource))
        close();
    if (!m_program->addShaderFromSourceCode(QOpenGLShader::Fragment,
                                            fragmentShaderSource))
        close();
    if (!m_program->link()) close();
}

void SurfaceGLWidget::initSurfaceData() {
    // 创建并绑定VAO
    m_vao.create();
    m_vao.bind();

    // 生成曲面数据
    updateSurfaceData();

    // 创建并绑定VBO
    m_vbo.create();
    m_vbo.bind();

    // 分配顶点和颜色数据
    int vertexCount = m_vertices.size() + m_colors.size();
    m_vbo.allocate(vertexCount * sizeof(GLfloat));
    m_vbo.write(0, m_vertices.constData(), m_vertices.size() * sizeof(GLfloat));
    m_vbo.write(m_vertices.size() * sizeof(GLfloat), m_colors.constData(),
                m_colors.size() * sizeof(GLfloat));

    // 设置顶点属性
    m_program->bind();
    m_program->enableAttributeArray("position");
    m_program->setAttributeBuffer("position", GL_FLOAT, 0, 3);
    m_program->enableAttributeArray("color");
    m_program->setAttributeBuffer("color", GL_FLOAT,
                                  m_vertices.size() * sizeof(GLfloat), 3);

    m_vao.release();
    m_vbo.release();
    m_program->release();
}

void SurfaceGLWidget::updateSurfaceData() {
    m_vertices.clear();
    m_colors.clear();

    const float xMin = -2.0f;
    const float xMax = 2.0f;
    const float yMin = -2.0f;
    const float yMax = 2.0f;
    const float dx   = (xMax - xMin) / (SURFACE_POINTS - 1);
    const float dy   = (yMax - yMin) / (SURFACE_POINTS - 1);

    // 生成顶点数据
    for (int i = 0; i < SURFACE_POINTS - 1; ++i) {
        float y = yMin + i * dy;
        for (int j = 0; j < SURFACE_POINTS; ++j) {
            float x = xMin + j * dx;

            // 计算第一个点
            float r  = sqrt(x * x + y * y);
            float z1 = r == 0 ? 1.0f : sin(r) / r;

            // 计算第二个点
            float y2 = y + dy;
            r        = sqrt(x * x + y2 * y2);
            float z2 = r == 0 ? 1.0f : sin(r) / r;

            // 添加顶点
            m_vertices << x << y << z1;
            m_vertices << x << y2 << z2;

            // 添加颜色
            float h1 = (z1 + 1.0f) / 2.0f;
            float h2 = (z2 + 1.0f) / 2.0f;

            m_colors << h1 << 0.5f << 1.0f - h1;
            m_colors << h2 << 0.5f << 1.0f - h2;
        }
    }
}
